import { ContributionCalendar } from 'configs/contributionsCollection';
import { ILocalStore } from 'hooks/useLocalStore';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import moment from 'moment';
import {
    ContributionsCollectionApi,
    normalizeContributionsCollection,
    normalizePopularUserRepos,
    PopularUserReposApi,
    PopularUserReposModel,
} from 'store/models';
import RootStore from 'store/RootStore';
import ApiStore, { FetchMeta } from 'store/RootStore/ApiStore';

type ContributionsCollectionRequest = {
    year: number;
    user: string;
};

type PrivateFields =
    | '_contributionsCalendar'
    | '_contributionsYears'
    | '_chosenYear'
    | '_contributionsCollectionMeta'
    | '_popularUserReposMeta'
    | '_popularUserRepos';

export default class UserPageStore implements ILocalStore {
    private readonly _ApiStore = new ApiStore();
    private _contributionsCalendar?: ContributionCalendar;
    private _contributionsYears: number[] = [];
    private _chosenYear: number = Number(moment().format('YYYY'));
    private _popularUserRepos: PopularUserReposModel[] = [];
    private _contributionsCollectionMeta: FetchMeta = FetchMeta.INITIAL;
    private _popularUserReposMeta: FetchMeta = FetchMeta.INITIAL;
    private readonly _fetchMetaReaction: IReactionDisposer;

    constructor() {
        makeObservable<UserPageStore, PrivateFields>(this, {
            _contributionsCalendar: observable.ref,
            _contributionsYears: observable.ref,
            _chosenYear: observable,
            _popularUserRepos: observable.ref,
            _contributionsCollectionMeta: observable,
            _popularUserReposMeta: observable,
            contributionsCalendar: computed,
            contributionsYears: computed,
            chosenYear: computed,
            popularUserRepos: computed,
            _fetchRequestsMeta: computed,
            fetchContributionsCollection: action.bound,
            fetchPopularUserRepos: action.bound,
            destroy: false,
        });

        this._fetchMetaReaction = reaction(
            () => this._fetchRequestsMeta,
            (meta) => {
                RootStore.pageLoader.getFetchRequestsMeta(meta);
            },
        );

        this.fetchPopularUserRepos(RootStore.githubOAuth.userOwnProfile?.login ?? '');
        this.fetchContributionsCollection({
            user: RootStore.githubOAuth.userOwnProfile?.login ?? '',
            year: Number(moment().format('YYYY')),
        });
    }

    get _fetchRequestsMeta(): FetchMeta[] {
        return [this._contributionsCollectionMeta, this._popularUserReposMeta];
    }
    get contributionsCalendar(): ContributionCalendar | undefined {
        return this._contributionsCalendar;
    }
    get contributionsYears(): number[] {
        return this._contributionsYears;
    }
    get chosenYear(): number {
        return this._chosenYear;
    }

    get popularUserRepos(): PopularUserReposModel[] {
        return this._popularUserRepos;
    }

    fetchPopularUserRepos = async (user: string): Promise<void> => {
        this._popularUserReposMeta = FetchMeta.FETCHING;
        try {
            const result = await this._ApiStore.getApiRequest<PopularUserReposApi>(
                `/search/repositories?q=user:${user}`,
                { params: { sort: 'stars', per_page: 6 } },
            );
            runInAction(() => {
                this._popularUserRepos = normalizePopularUserRepos(result.data);
                this._popularUserReposMeta = FetchMeta.FETCHED;
            });
        } catch {
            runInAction(() => {
                this._popularUserReposMeta = FetchMeta.ERROR;
                this._popularUserRepos = [];
            });
        }
    };

    fetchContributionsCollection = async (data: ContributionsCollectionRequest): Promise<void> => {
        this._contributionsCollectionMeta = FetchMeta.FETCHING;
        this._chosenYear = data.year;
        const queryString = `
          query {
            user(login:"${data.user}"){
              contributionsCollection(from:"${moment(data.year, 'YYYY')
                  .startOf('year')
                  .startOf('week')
                  .format()}" to:"${moment(data.year, 'YYYY').endOf('year').format()}"){
                contributionYears
                contributionCalendar{
                  totalContributions
                  weeks{
                    firstDay
                    contributionDays{
                      color
                      contributionCount
                      date
                    }
                  }
                }
              }
            }
          }`;

        try {
            const result = await this._ApiStore.postApiRequest<ContributionsCollectionApi, unknown>(`/graphql`, {
                query: queryString,
            });
            runInAction(() => {
                if (JSON.stringify(result.data).includes('errors') || JSON.stringify(result.data).includes('error')) {
                    this._contributionsCollectionMeta = FetchMeta.ERROR;
                } else {
                    const normalize = normalizeContributionsCollection(result.data);
                    this._contributionsCalendar = normalize.contributionCalendar;
                    this._contributionsYears = normalize.contributionYears.slice(0, 3);
                    this._contributionsCollectionMeta = FetchMeta.FETCHED;
                }
            });
        } catch {
            action('fetchError', () =>
                runInAction(() => {
                    this._contributionsCollectionMeta = FetchMeta.ERROR;
                    this._contributionsCalendar = undefined;
                    this._contributionsYears = [];
                }),
            );
        }
    };

    destroy(): void {
        this._fetchMetaReaction();
    }
}
